import EventTarget from '../libs/EventTarget.js';
import { decode as base64ToArrayBuffer } from '../libs/base64-arraybuffer';

const _requestHeader = new WeakMap();
const _responseHeader = new WeakMap();
const _requestTask = new WeakMap();

function _triggerEvent(type, event = {}) {
  event.target = event.target || this;

  if (typeof this[`on${type}`] === 'function') {
    this[`on${type}`].call(this, event);
  }
}

function _changeReadyState(readyState, event = {}) {
  this.readyState = readyState;

  event.readyState = readyState;

  _triggerEvent.call(this, 'readystatechange', event);
}

function _isRelativePath(url) {
  return !/^(http|https|ftp|myfile):\/\/.*/i.test(url);
}

export default class $XMLHttpRequest extends EventTarget {
  constructor() {
    super();

    /*
     * TODO 这一批事件应该是在 XMLHttpRequestEventTarget.prototype 上面的
     */
    this.onabort = null;
    this.onerror = null;
    this.onload = null;
    this.onloadstart = null;
    this.onprogress = null;
    this.ontimeout = null;
    this.onloadend = null;

    this.onreadystatechange = null;
    this.readyState = 0;
    this.response = null;
    this.responseText = null;
    this.responseType = 'text';
    this.dataType = 'arraybuffer';
    this.responseXML = null;
    this.status = 0;
    this.statusText = '';
    this.upload = {};
    this.withCredentials = false;

    _requestHeader.set(this, {
      'content-type': 'application/x-www-form-urlencoded',
    });
    _responseHeader.set(this, {});
  }

  abort() {
    const myRequestTask = _requestTask.get(this);

    if (myRequestTask) {
      myRequestTask.abort();
    }
  }

  getAllResponseHeaders() {
    const responseHeader = _responseHeader.get(this);

    return Object.keys(responseHeader)
      .map(header => {
        return `${header}: ${responseHeader[header]}`;
      })
      .join('\n');
  }

  getResponseHeader(header) {
    return _responseHeader.get(this)[header];
  }

  open(method, url) {
    this._method = method;
    this._url = url;
    _changeReadyState.call(this, $XMLHttpRequest.OPENED);
  }

  overrideMimeType() {}

  async send(data = '') {
    if (this.readyState !== $XMLHttpRequest.OPENED) {
      throw new Error(
        "Failed to execute 'send' on 'XMLHttpRequest': The object's state must be OPENED.",
      );
    } else {
      const url = this._url;
      const header = _requestHeader.get(this);
      const responseType = this.responseType;
      const dataType = this.dataType;

      const relative = _isRelativePath(url);
      let encoding;

      if (responseType === 'arraybuffer') {
        // encoding = 'binary'
      } else {
        encoding = 'utf8';
      }

      delete this.response;
      this.response = null;

      const onSuccess = ({ data, statusCode, header }) => {
        if (responseType === 'arraybuffer') {
          data = base64ToArrayBuffer(data);
        }

        statusCode = statusCode === undefined ? 200 : statusCode;
        if (typeof data !== 'string' && !(data instanceof ArrayBuffer)) {
          try {
            data = JSON.stringify(data);
          } catch (e) {}
        }

        this.status = statusCode;
        if (header) {
          _responseHeader.set(this, header);
        }
        _triggerEvent.call(this, 'loadstart');
        _changeReadyState.call(this, $XMLHttpRequest.HEADERS_RECEIVED);
        _changeReadyState.call(this, $XMLHttpRequest.LOADING);

        this.response = data;

        if (data instanceof ArrayBuffer) {
          Object.defineProperty(this, 'responseText', {
            enumerable: true,
            configurable: true,
            get: function () {
              throw 'InvalidStateError : responseType is ' + this.responseType;
            },
          });
        } else {
          this.responseText = data;
        }
        _changeReadyState.call(this, $XMLHttpRequest.DONE);
        _triggerEvent.call(this, 'load');
        _triggerEvent.call(this, 'loadend');
      };

      const onFail = ({ errorMessage }) => {
        // TODO 规范错误

        if (errorMessage.indexOf('abort') !== -1) {
          _triggerEvent.call(this, 'abort');
        } else {
          _triggerEvent.call(this, 'error', {
            message: errorMessage,
          });
        }
        _triggerEvent.call(this, 'loadend');

        if (relative) {
          // 用户即使没监听error事件, 也给出相应的警告
          console.warn(errorMessage);
        }
      };

      if (relative) {
        const fs = my.getFileSystemManager();

        var options = {
          filePath: url,
          success: onSuccess,
          fail: onFail,
        };
        // if (encoding) {
        // options["encoding"] = encoding;
        // }
        fs.access({
          path: url,
          success: res => {
            console.log('文件存在', res);
          },
          fail: err => {
            console.log('err:', err);
          },
        });
        fs.readFile(options);
        return;
      }

      if ($XMLHttpRequest.URLModifier) url = await $XMLHttpRequest.URLModifier(url);

      my.downloadFile({
        url,
        data,
        success: ({ apFilePath }) => {
          const fs = my.getFileSystemManager();
          fs.readFile({
            filePath: apFilePath,
            encoding: responseType === 'arraybuffer' ? 'base64' : 'utf8',
            // encoding: 'arraybuffer', // 不写encoding默认ArrayBuffer
            success: onSuccess,
          });
        },
        fail: onFail,
      });

      // my.request({
      //   data,
      //   url: url,
      //   method: this._method,
      //   header: header,
      //   dataType: dataType,
      //   responseType: responseType,
      //   success: onSuccess,
      //   fail: onFail,
      // });
    }
  }

  setRequestHeader(header, value) {
    const myHeader = _requestHeader.get(this);

    myHeader[header] = value;
    _requestHeader.set(this, myHeader);
  }

  addEventListener(type, listener) {
    if (typeof listener !== 'function') {
      return;
    }

    this['on' + type] = (event = {}) => {
      event.target = event.target || this;
      listener.call(this, event);
    };
  }

  removeEventListener(type, listener) {
    if (this['on' + type] === listener) {
      this['on' + type] = null;
    }
  }
}

// TODO 没法模拟 HEADERS_RECEIVED 和 LOADING 两个状态
$XMLHttpRequest.UNSEND = 0;
$XMLHttpRequest.OPENED = 1;
$XMLHttpRequest.HEADERS_RECEIVED = 2;
$XMLHttpRequest.LOADING = 3;
$XMLHttpRequest.DONE = 4;
